package com.smmisia.shop.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.smmisia.common.Result.Result;
import com.smmisia.shop.api.AuthService;
import com.smmisia.shop.api.User;
import com.smmisia.shop.constant.*;
import com.smmisia.shop.entity.Product;
import com.smmisia.shop.entity.TradeOrder;
import com.smmisia.shop.entity.TradeOrderStream;
import com.smmisia.shop.entity.convetor.TradeOrderConvertor;
import com.smmisia.shop.event.OrderCreateEvent;
import com.smmisia.shop.exception.ProductException;
import com.smmisia.shop.exception.TradeErrorCode;
import com.smmisia.shop.mapper.ProductMapper;
import com.smmisia.shop.mapper.TradeOrderStreamMapper;
import com.smmisia.shop.model.ProductVO;
import com.smmisia.shop.model.TradeOrderVO;
import com.smmisia.shop.model.dto.OrderStreamDTO;
import com.smmisia.shop.param.BuyParam;
import com.smmisia.shop.param.CancelParam;
import com.smmisia.shop.producer.StreamProducer;
import com.smmisia.shop.request.*;
import com.smmisia.shop.response.OrderResponse;
import com.smmisia.shop.response.ProductInventoryResponse;
import com.smmisia.shop.service.ProductInventoryStreamService;
import com.smmisia.shop.service.ProductService;
import com.smmisia.shop.service.TradeOrderService;
import com.smmisia.shop.mapper.TradeOrderMapper;
import com.smmisia.shop.sharding.id.DistributeID;
import com.smmisia.shop.sharding.id.WorkerIdHolder;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.smmisia.shop.constant.UserType.PLATFORM;
import static com.smmisia.shop.exception.TradeErrorCode.*;

@Slf4j
@Service
public class TradeOrderServiceImpl extends ServiceImpl<TradeOrderMapper, TradeOrder>
    implements TradeOrderService{

    @Autowired
    private ProductService productService;

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private ProductInventoryStreamService productInventoryStreamService;

    @Autowired
    private TradeOrderMapper orderMapper;

    @Autowired
    private TradeOrderStreamMapper orderStreamMapper;

    @Autowired
    protected ApplicationContext applicationContext;

    @Autowired
    private AuthService authService;

    @Autowired
    private StreamProducer streamProducer;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Override
    public Result<OrderStreamDTO> buy(BuyParam buyParam) {
        OrderCreateRequest orderCreateRequest = getOrderCreateRequest(buyParam);
        OrderStreamDTO dto = preDeduct(orderCreateRequest);
        return Result.ok(dto);
    }

    @Override
    public Result<TradeOrderVO> orderList(Integer pageNum, Integer pageSize) {
        String userId = (String) StpUtil.getLoginId();
        LambdaQueryWrapper<TradeOrder> wrapper = new LambdaQueryWrapper<TradeOrder>();
        wrapper.eq(TradeOrder::getBuyerId, userId)
                .eq(TradeOrder::getDeleted, 0)
                .orderByDesc(TradeOrder::getGmtModified);
        List<TradeOrder> list = orderMapper.selectList(wrapper);
        if (list.isEmpty()) {
            return Result.ok();
        }
        List<TradeOrderVO> voList = TradeOrderConvertor.INSTANCE.mapToVo(list);

        Map<Integer, User> userMap = list.stream()
                .flatMap(order -> Stream.of(order.getBuyerId(), order.getSellerId()))
                .map(Integer::valueOf)
                .distinct()
                .collect(Collectors.toMap(id -> id, authService::info));

        voList.forEach(order -> {
            Integer buyerId = Integer.valueOf(order.getBuyerId());
            User buyUser = userMap.get(buyerId);
            if (buyUser != null) {
                order.setBuyerName(buyUser.getUsername());
            }
            Integer sellId = Integer.valueOf(order.getSellerId());
            User sellUser = userMap.get(sellId);
            if (sellUser != null) {
                order.setSellerName(sellUser.getUsername());
            }
        });

        return Result.ok(voList);
    }

    @Override
    public Result<Boolean> cancel(CancelParam cancelParam) {
        String loginId = (String) StpUtil.getLoginId();
        OrderCancelRequest request = new OrderCancelRequest();
        request.setOrderId(cancelParam.getOrderId());
        request.setIdentifier(cancelParam.getOrderId());
        request.setOperator(loginId);
        TradeOrder tradeOrder = orderMapper.selectByOrderId(cancelParam.getOrderId());
        if(tradeOrder == null){
            log.error("订单不存在，订单号:{}", cancelParam.getOrderId());
            throw new ProductException(ORDER_NOT_EXIST);
        }
        if(tradeOrder.getOrderState().equals(TradeOrderState.CLOSED)){
            return Result.ok("订单已关闭");
        }
        Boolean message = SendMessageForClose(request);
        if (message){
            return Result.ok("订单取消成功");
        }
        log.error("订单取消失败，订单号:{}", cancelParam.getOrderId());
        throw new ProductException(ORDER_CANCEL_FAILED);
    }

    @Override
    public Result<TradeOrderVO> orderDetail(String orderId) {
        String loginId = (String) StpUtil.getLoginId();
        TradeOrder tradeOrder = orderMapper.selectByOrderAndBuyerId(orderId, loginId);
        if(tradeOrder == null){
            log.error("订单不存在，订单号:{}", orderId);
            throw new ProductException(ORDER_NOT_EXIST);
        }
        TradeOrderVO tradeOrderVO = TradeOrderConvertor.INSTANCE.mapToVo(tradeOrder);

        //如果订单已经超时,且未关闭，则进行主动关单
        if(tradeOrderVO.getTimeout() && tradeOrderVO.getOrderState() == TradeOrderState.CONFIRM){
            OrderTimeoutRequest request = new OrderTimeoutRequest();
            request.setOperatorType(PLATFORM.name());
            request.setOperator(loginId);
            request.setOrderId(tradeOrderVO.getOrderId());
            request.setOperateTime(new Date());
            request.setIdentifier(UUID.randomUUID().toString());
            Boolean message = SendMessageForClose(request);
            if (message){
                //再查询一次确定订单是否关单成功
                tradeOrder = orderMapper.selectByOrderAndBuyerId(orderId, loginId);
                tradeOrderVO = TradeOrderConvertor.INSTANCE.mapToVo(tradeOrder);
                if (tradeOrder.getOrderState() == TradeOrderState.CLOSED){
                    return Result.ok(tradeOrderVO);
                }
            }
            log.error("订单主动关单失败，订单号:{}", orderId);
            throw new ProductException(ORDER_CLOSE_FAIL);
        }
        return Result.ok(tradeOrderVO);
    }

    /**
     * 发送消息
     * @param request
     * @return
     */
    public Boolean SendMessageForClose(BaseOrderUpdateRequest request){
        boolean result = streamProducer.send("orderClose-out-0", null, JSON.toJSONString(request), "CLOSE_TYPE", request.getOrderEvent().name());
        if (result){
            return true;
        }
        return false;
    }

    /**
     * 关闭订单
     * @param request
     * @return
     */
    @Override
    public OrderResponse cancel(OrderCancelRequest request) {
        return doExecute(request, tradeOrder -> tradeOrder.close(request));
    }

    /**
     * 订单超时
     *
     * @param request
     * @return
     */
    public OrderResponse timeout(OrderTimeoutRequest request) {
        return doExecute(request, tradeOrder -> tradeOrder.close(request));
    }

    @Override
    public OrderResponse confirm(OrderConfirmRequest request) {
        return doExecute(request, tradeOrder -> tradeOrder.confirm(request));
    }

    protected OrderResponse doExecute(BaseOrderUpdateRequest orderRequest, Consumer<TradeOrder> consumer) {
        TradeOrder existOrder = orderMapper.selectByOrderId(orderRequest.getOrderId());
        if (existOrder == null) {
            log.error("订单不存在，订单号:{}", orderRequest.getOrderId());
            throw new ProductException(ORDER_NOT_EXIST);
        }
        if(!hasPermission(existOrder,orderRequest)){
            log.error("无权限操作，订单号:{}", orderRequest.getOrderId());
            throw new ProductException(TradeErrorCode.PAY_PERMISSION_DENIED);
        }
        TradeOrderStream existStream = orderStreamMapper.selectByIdentifier(orderRequest.getIdentifier(), orderRequest.getOrderEvent().name(), orderRequest.getOrderId());
        if (existStream != null){
            log.error("订单已关闭，订单号:{}", orderRequest.getOrderId());
            throw new ProductException(ORDER_CANCEL_FAILED);
        }

        //核心逻辑执行
        consumer.accept(existOrder);

        Long streamId = transactionTemplate.execute(transactionStatus -> {
            boolean result = orderMapper.updateByOrderId(existOrder) == 1;
            Assert.isTrue(result, () -> new ProductException(OrderErrorCode.UPDATE_ORDER_FAILED));
            TradeOrderStream orderStream = new TradeOrderStream(existOrder, orderRequest.getOrderEvent(), orderRequest.getIdentifier());
            result = orderStreamMapper.insert(orderStream) == 1;
            Assert.isTrue(result, () -> new ProductException(ORDER_CANCEL_FAILED));
            return orderStream.getId();
        });

        OrderResponse orderResponse = new OrderResponse();
        orderResponse.setOrderId(existOrder.getOrderId());
        orderResponse.setStreamId(streamId.toString());
        orderResponse.setSuccess(true);
        return orderResponse;


    }


    public Boolean hasPermission(TradeOrder order,BaseOrderUpdateRequest request){
        TradeOrderEvent orderEvent = request.getOrderEvent();
        switch(orderEvent){
            case PAY:
            case CANCEL:
                return order.getBuyerId().equals(request.getOperator());
            case TIME_OUT:
            case CONFIRM:
            case FINISH:
                return request.getOperatorType().equals(UserType.PLATFORM.name());
            default:
                throw new UnsupportedOperationException("unsupported orderEvent " + orderEvent);
        }
    }


    private OrderCreateRequest getOrderCreateRequest(BuyParam buyParam) {
        String userId = (String) StpUtil.getLoginId();
        String orderId = DistributeID.generateWithSnowflake(BusinessCode.TRADE_ORDER, WorkerIdHolder.WORKER_ID, userId);
        Product product = productMapper.selectById(buyParam.getProductId());
        //创建订单
        OrderCreateRequest orderCreateRequest = new OrderCreateRequest();
        orderCreateRequest.setOrderId(orderId);
        orderCreateRequest.setIdentifier(orderId);
        orderCreateRequest.setBuyerId(userId);
        orderCreateRequest.setProductId(buyParam.getProductId());
        orderCreateRequest.setProductType(product.getClassId());
        orderCreateRequest.setItemCount(buyParam.getItemCount());
        ProductVO productVO = queryById(Long.valueOf(buyParam.getProductId()));
        if(productVO == null || productVO.getState()== ProductVoState.NOT_FOR_SALE){
            log.error("商品不可售卖，商品ID:{}", buyParam.getProductId());
            throw new ProductException(TradeErrorCode.GOODS_NOT_FOR_SALE);
        }
        orderCreateRequest.setItemPrice(productVO.getPrice());
        orderCreateRequest.setSellerId(String.valueOf(productVO.getSellerId()));
        orderCreateRequest.setProductName(productVO.getName());
        orderCreateRequest.setProductPicUrl(productVO.getImages());
        orderCreateRequest.setOrderAmount(orderCreateRequest.getItemPrice().multiply(new BigDecimal(orderCreateRequest.getItemCount())));
        return orderCreateRequest;
    }


    private ProductVO queryById(Long productId){
        Product product = productMapper.selectById(productId);
        InventoryRequest request = new InventoryRequest();
        request.setProductId(String.valueOf(productId));
        Integer inventory = productInventoryStreamService.getInventory(request);
        if(inventory == null){
            inventory = 0;
        }
        ProductVO productVO = productService.queryById(productId);
        productVO.setQuantity(Long.valueOf(inventory));
        LocalDateTime saleTime = product.getSaleTime();
        ZonedDateTime zonedDateTime = saleTime.atZone(ZoneId.systemDefault());
        Instant instant = zonedDateTime.toInstant();
        Date date = Date.from(instant);
        productVO.setState(product.getState(),date, inventory.longValue());
        return productVO;
    }

    public OrderStreamDTO preDeduct(OrderCreateRequest orderCreateRequest) {
        InventoryRequest inventoryRequest = new InventoryRequest();
        inventoryRequest.setProductId(orderCreateRequest.getProductId());
        inventoryRequest.setIdentifier(orderCreateRequest.getOrderId());
        inventoryRequest.setInventory(orderCreateRequest.getItemCount());
        ProductInventoryResponse decrease = productInventoryStreamService.decrease(inventoryRequest);
        if(decrease.getSuccess()){
            //创建订单
            OrderStreamDTO dto = create(orderCreateRequest);
            return dto;
        }
        log.error("库存不足，商品ID:{},订单ID:{}", orderCreateRequest.getProductId(), orderCreateRequest.getOrderId());
        throw new ProductException(TradeErrorCode.ORDER_CREATE_FAILED);
    }

    @Transactional(rollbackFor = Exception.class)
    public OrderStreamDTO create(OrderCreateRequest request) {
        TradeOrder existOrder = orderMapper.selectByIdentifier(request.getIdentifier(), request.getBuyerId());
        if (existOrder != null) {
            log.error("订单已存在，订单号:{}", request.getOrderId());
            throw new ProductException(ORDER_HAVE_EXIST);
        }
        TradeOrder order = TradeOrder.createOrder(request);
        boolean save = orderMapper.insert(order) == 1;
        Assert.isTrue(save, () -> new ProductException(TradeErrorCode.ORDER_CREATE_FAILED));
        TradeOrderStream orderStream = new TradeOrderStream(order, request.getOrderEvent(), request.getIdentifier());
        save = orderStreamMapper.insert(orderStream) == 1;
        Assert.isTrue(save, () -> new ProductException(TradeErrorCode.ORDER_CREATE_FAILED));
        applicationContext.publishEvent(new OrderCreateEvent(order));
        OrderStreamDTO dto = new OrderStreamDTO();
        dto.setOrderId(order.getOrderId());
        dto.setStreamId(String.valueOf(orderStream.getId()));
        return dto;
    }


}




